<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-GB">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <meta http-equiv="pragma" content="no-cache" />
        <meta http-equiv="Expires" content="-1" />
        <meta name="apple-mobile-web-app-capable" content="yes" />
        <meta name="apple-mobile-web-app-status-bar-style" content="black" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />

        <link rel="apple-touch-startup-image" href="/public/images/el_portrait.png" />
        <link rel="apple-touch-icon" href="/public/images/el_touch_icon.png" />

        <script src="/js/greyspots.js" type="text/javascript"></script>
        <link href="/css/greyspots.css" type="text/css" rel="stylesheet">

		<link href="../css/postage.css" type="text/css" rel="stylesheet" />
		<link href="resources/pg-9.2-other.css" type="text/css" rel="stylesheet" />

        <script src="/js/d3.js"></script>

        <title>Statistics</title>
        <style>
        /*
            .axis--x path {
                display: none;
            }
            */
            gs-chart-line {
                display: block;
                width: 100%;
                height: 100%;
            }

            gs-chart-line .line {
                fill: none;
                stroke-width: 1.5px;
            }
        </style>
        <script>
/*jslint browser*/
/*global xtag, window, GS, d3, console, ml*/



/*window.addEventListener('resize', function () {
    var master_elements = document.getElementsByClassName('master_g');
    for (var i = 0, len = master_elements.length; i < len; i += 1) {
        var element = master_elements[i].parentNode.parentNode.parentNode;
        //master_elements[i].offsetWidth
        //master_elements[i].setAttribute("transform", "translate(" + elementforresize.margin.left + "," + elementforresize.margin.top + ")");
    }
});*/



document.addEventListener('DOMContentLoaded', function () {
    'use strict';
    function pushReplacePopHandler(element) {

    }

    // dont do anything that modifies the element here
    function elementCreated(element) {
        // if "created" hasn't been suspended: run created code
        if (!element.hasAttribute('suspend-created')) {

        }
    }

	var _parseTime = d3.timeParse('%Y-%m-%dT%H:%M:%S'), dateFormat = d3.timeFormat('%Y-%m-%dT'),
		parseTime = function (strTime) {
			var d = new Date();
			var strDate = dateFormat(d);

			return _parseTime(strDate + strTime);
		};

    function elementInterval(element) {
		var discard = document.getElementById('discard');
		if (discard) {
			var intDiscard = parseInt(discard.value, 10) * 1000, d = new Date();
			while (element.rowData[0] && (d.getTime() - parseTime(element.rowData[0][0].substring(0, 8)).getTime()) > intDiscard) {
				element.rowData.shift();
			}
		}

        var d3_element = d3.select(element);

        //prepare arr_d3_data with data in format that lines will understand
        var x_len = element.rowData.length;
        var x = 0;
        var i_len = element.rowData[0].length;
        var i = 1; //first column is time

        //console.log('element.rowData', element.rowData);
        //console.log('i_len', i_len);

        //fill array of d3 data
        element.arr_d3_data = [];
        while (i < i_len) {
            element.arr_d3_data[i - 1] = [];

            x = 0;
            while (x < x_len) {
				//console.log(parseTime);
				//console.log(element.rowData[x][0].substring(0, 8));
				//console.log(parseTime(element.rowData[x][0].substring(0, 8)));
                element.arr_d3_data[i - 1].push({
                    x: parseTime(element.rowData[x][0].substring(0, 8)),
                    y: element.rowData[x][i]
                });

                x += 1;
            }

            i += 1;
        }

        //console.log('element.arr_d3_data', element.arr_d3_data);

        //set max and min of axes
        //console.log('element.extent_x', element.extent_x);
        element.x.domain(element.extent_x);
        //console.log('element.extent_y', element.extent_y);
        element.y.domain(element.extent_y);

        //reset x axis
        element.axis_x
            .call(d3.axisBottom(element.x).ticks(10).tickFormat(d3.timeFormat('%H:%M:%S')))
			.selectAll("text")
		        .style("text-anchor", "end")
		        .attr("dx", "-.8em")
		        .attr("dy", ".15em")
                .style("font-size", "16px")
		        .attr("transform", "rotate(-65)");
        element.axis_x.enter();
        element.axis_x.exit();

        //reset y axis
        element.axis_y
            .call(d3.axisLeft(element.y));
        element.axis_y.enter();
        element.axis_y.exit();

        //append line labels
        var caption_i = d3_element.selectAll("text.caption").size();
        var caption_len = element.line_captions.length;
        var bbox;

        while (caption_i < caption_len) {
            bbox = element.axis_y.append("text")
                .attr("id", "text_" + element.arr_column[caption_i])
                .attr("class", "caption")
                .attr("fill", element.line_colors[caption_i])
                //.attr("transform", "rotate(-90)")
                .attr("x", element.label_x)
                .attr("y", -17)
                .attr("dy", "0.71em")
                .style("text-anchor", "start")
                .style("font-family", "Helvetica")
                .style("font-weight", "bold")
                .style("font-size", "20px")
                .text(element.line_captions[caption_i])
                .node().getBBox();

            element.label_x += bbox.width + 5;

            caption_i += 1;
        }

        //append lines
        i = 0;
        i_len = d3_element.selectAll("path.line").size();
        var objCurrentLine;

        while (i < (element.arr_column.length - 1)) {
            if (i < i_len) {
                objCurrentLine = element.g.select("#line_" + element.arr_column[i]);
            } else {
                objCurrentLine = element.g.append("path");
            }
			//console.log(element.line_f);
			//console.log(i, element.arr_d3_data[i]);
            objCurrentLine
                .attr("id", "line_" + element.arr_column[i])
                .attr("class", "line line_" + element.arr_column[i])
                .attr("style", "stroke: " + element.line_colors[i] + ";")
                .attr("stroke", element.line_colors[i])
                .datum(element.arr_d3_data[i])
                .attr("d", element.line_f);
            i += 1;
        }

    }

    //hand the ajax style data
    function handleData(element, data) {
        //document.getElementById('result').textContent = JSON.stringify(data.dat);

        //append means the difference between records is what we need to show
        if (element.hasAttribute('append')) {
            if (element.lastRowData) {
                var arrCur = data.dat[0];
                var arrPrev = element.lastRowData;
                var arrDiff = [arrCur[0]];

				// the first column is the time
                var i = 1;
                var len = data.dat[0].length;
                while (i < len) {
                    arrDiff[i] = arrCur[i] - arrPrev[i];
                    i += 1;
                }
                //console.log('arrCur', arrCur);
                //console.log('arrPrev', arrPrev);
                //console.log('arrDiff', arrDiff);

                //save the last record
                element.lastRowData = arrCur;

                //add to beginning of array the difference between the last two records
                element.rowData.push(arrDiff);
				//console.log(data);

                element.arr_column = data.arr_column;

                //remove any extraneous records
                while (element.hasAttribute('extent_x') && element.rowData.length > element.extent_x_diff + 1) {
                    element.rowData.shift();
                }

                // console.log('element.rowData', element.rowData);

                //run interval function
                elementInterval(element);
            } else {
                //save the last record
                element.lastRowData = data.dat[0];
            }

        } else {
            //add to beginning of array the new record
            element.rowData.push(data.dat[0]);
			//console.log(data);

            element.arr_column = data.arr_column;

            //console.log('element.extent_x'     , element.extent_x     );
            //console.log('element.extent_x_diff', element.extent_x_diff);

            //remove and extraneous records
            while (element.hasAttribute('extent_x') && element.rowData.length > element.extent_x_diff + 1) {
                element.rowData.shift();
            }

            // console.log('element.rowData', element.rowData);

            //run interval function
            elementInterval(element);
        }
    }

	function rawIntervalFunction(element) {
		var arrTotalRecords = [];
		GS.requestRawFromSocket(GS.envSocket,
			element.getAttribute('raw'),
			function (data, error) {
				if (!error) {
					if (data.strMessage !== '\\.') {
						var arrRecords = GS.trim(data.strMessage, '\n').split('\n');

						var cell_i;
						var cell_len;
						var arrCells;

						var i = 0;
						var len = arrRecords.length;
						while (i < len) {
							arrCells = arrRecords[i].split('\t');

							cell_i = 0;
							cell_len = arrCells.length;
							while (cell_i < cell_len) {
								arrCells[cell_i] = GS.decodeFromTabDelimited(arrCells[cell_i]);
								cell_i += 1;
							}

							arrTotalRecords.push(arrCells);
							i += 1;
						}
					} else {
						// console.log('data', data);
						// console.log('arrTotalRecords', arrTotalRecords);
						element.arrColumns = data.arrColumnNames;

						handleData(element, {
							"arr_column": data.arrColumnNames
						  , "dat": arrTotalRecords
						  , "row_count": arrTotalRecords.length
						});
					}

				} else {
					GS.ajaxErrorDialog(data);
				}
			}
		);
	}

    //
    function elementInserted(element) {
        // if this is the first time inserted has been run: continue

        if (!element.inserted) {
            element.inserted = true;

			//console.log('test');
			element.strIntervalInputID = 'refresh_interval' + Math.random().toString().substring(2);

            //fill gs-chart-line with svg
            element.innerHTML = ml(function () {/*
    <div class="root">
		<div flex-horizontal>
			<div style="width: 2em;"></div>
			<div flex>
				<label for="{{ID}}">Interval (seconds):</label>
				<gs-text id="{{ID}}" value="1" class="refresh_interval"></gs-text>
			</div>
		</div>
        <svg width="100%" height="100%"></svg>
    </div>*/}).replace(/\{\{ID\}\}/g, element.strIntervalInputID);

            //prepare svg
            element.d3_element = d3.select(element);
            element.svg = element.d3_element.select("svg");

            element.margin = {top: 20, right: 20, bottom: 85, left: 60};

            var rect = element.svg.node().getBoundingClientRect();

            element.width = +rect.width - element.margin.left - element.margin.right;
            element.height = +rect.height - element.margin.top - element.margin.bottom;

            //add g element
            element.g = element.svg.append("g").attr("transform", "translate(" + element.margin.left + "," + element.margin.top + ")")
                .attr("id", "master_g")
                .attr("class", "master_g");



            //set x and y variable
            element.x = d3.scaleTime()
                .rangeRound([0, element.width]);

            element.y = d3.scaleLinear()
                .rangeRound([element.height, 0]);


            window.addEventListener('resize', function () {
                var rect = element.svg.node().getBoundingClientRect();

                element.width = +rect.width - element.margin.left - element.margin.right;
                element.height = +rect.height - element.margin.top - element.margin.bottom;

                //set x and y variable
                element.x = d3.scaleTime()
                    .rangeRound([0, element.width]);

                element.y = d3.scaleLinear()
                    .rangeRound([element.height, 0]);
            });

            //create line functions using the x and y variables
            element.line_f = d3.line()
                .x(function (d) {
					//console.log(d, element.x(new Date(d.x)));
                    return element.x(new Date(d.x));
                })
                .y(function (d) {
                    return element.y(d.y);
                });

            //set min/max of data and x/y variables
            element.arr_d3_data = [];
            //console.log('element.extent_x', element.extent_x);
            element.x.domain(element.extent_x);
            //console.log('element.extent_y', element.extent_y);
            element.y.domain(element.extent_y);

            //append x axis
            element.axis_x = element.g.append("g")
                .attr("id", "axis_x")
                .attr("class", "axis axis--x")
                .attr("transform", "translate(0," + element.height + ")")
                .call(d3.axisBottom(element.x));

            //append y axis
            element.axis_y = element.g.append("g")
                .attr("id", "axis_y")
                .attr("class", "axis axis--y")
                .call(d3.axisLeft(element.y));

            //create row data
            element.rowData = [];

            //create chart caption
            element.label_x = 25;
            var bbox = element.axis_y.append("text")
                    .attr("id", "text_chart_caption")
                    .attr("class", "chart_caption")
                    .attr("fill", "#000000")
                    //.attr("transform", "rotate(-90)")
                    .attr("x", element.label_x)
                    .attr("y", -17)
                    .attr("dy", "0.71em")
                    .style("text-anchor", "start")
                    .style("font-family", "Helvetica")
                    .style("font-weight", "bold")
                    .style("font-size", "20px")
                    .text(element.getAttribute('chart_caption'))
                    .node().getBBox();

            element.label_x = element.label_x + bbox.width + 20;

            //if type is live

            //if raw="" is set
            if (element.hasAttribute('raw')) {
                //then set an interval to get the data every second
                element.interval = setInterval(rawIntervalFunction, 1000, element);

				document.getElementById(element.strIntervalInputID).addEventListener('keyup', function () {
					clearInterval(element.interval);
	                element.interval = setInterval(rawIntervalFunction, parseFloat(this.value, 1) * 1000, element);
				});
				document.getElementById(element.strIntervalInputID).addEventListener('change', function () {
					clearInterval(element.interval);
	                element.interval = setInterval(rawIntervalFunction, parseFloat(this.value, 1) * 1000, element);
				});

			} else {
				console.error('raw attribute not set');
			}
        }
    }

/*
<gs-chart-line type="live" extent_x="0,30" line_captions="Total,Active,Idle" line_colors="#FF8888,#88FF88,#8888FF" src="(SELECT count(*) AS total_connections, sum(CASE WHEN state = 'active' THEN 1 ELSE 0 END) AS active_connections, sum(CASE WHEN state = 'active' THEN 0 ELSE 1 END) AS inactive_connections FROM pg_catalog.pg_stat_activity) em"></gs-chart-line>

<svg width="960" height="500">
</svg>

<gs-chart-line type="" x_column="" y_column="" extent_x="" extent_y="" line_captions="" line_captions="" where="" limit="" offset="" ord="" cols="" src=""></gs-chart-line>
*/

    xtag.register('gs-chart-line', {
        lifecycle: {
            created: function () {
                elementCreated(this);
            },

            inserted: function () {
                elementInserted(this);
            },

            attributeChanged: function (strAttrName, oldValue, newValue) {
                // if "suspend-created" has been removed: run created and inserted code
                if (strAttrName === 'suspend-created' && newValue === null) {
                    elementCreated(this);
                    elementInserted(this);

                // if "suspend-inserted" has been removed: run inserted code
                } else if (strAttrName === 'suspend-inserted' && newValue === null) {
                    elementInserted(this);

                } else if (!this.hasAttribute('suspend-created') && !this.hasAttribute('suspend-inserted')) {
                    // attribute code
                }
            }
        },
        events: {

        },
        accessors: {
            extent_x: {
                // get extent straight from the attribute
                get: function () {
                    if (this.hasAttribute('extent_x')) {
                        var x_arr = this.getAttribute('extent_x').split(',');

                        var x_i = 0;
                        var x_len = x_arr.length;

                        while (x_i < x_len) {
                            x_arr[x_i] = parseInt(x_arr[x_i], 10);
                            x_i += 1;
                        }

                        return x_arr;
                    } else {
						//get the minimum and maximum all the lines and set the minimum and maximum of the chart
                        var extent_i = 0;
                        var extent_len = this.arr_d3_data.length;
                        var curExtent = [];
                        var oldExtent;
                        var extent = [];

                        while (extent_i < extent_len) {
                            curExtent = d3.extent(this.arr_d3_data[extent_i], function (d) {
                                return d.x.getTime();
                            });
							//console.log(curExtent);

                            if (oldExtent) {
                                extent[0] = new Date(Math.min(curExtent[0], oldExtent[0], curExtent[1], oldExtent[1]));
                                extent[1] = new Date(Math.max(curExtent[0], oldExtent[0], curExtent[1], oldExtent[1]));
                            } else {
                                extent[0] = new Date(Math.min(curExtent[0], curExtent[1]));
                                extent[1] = new Date(Math.max(curExtent[0], curExtent[1]));
                            }
							//console.log(extent);

                            oldExtent = extent;

                            extent_i += 1;
                        }

                        if (this.hasAttribute('extent_x_reverse')) {
                            var temp = extent[0];
                            extent[0] = extent[1];
                            extent[1] = temp;
                        }

                        return extent;
                    }
                },

                // set the extent attribute
                set: function (newValue) {
                    this.setAttribute('extent_x', newValue);
                }
            },
            extent_x_diff: {
                // get value from the other accessor
                get: function () {
                    var x_arr = this.extent_x;
                    if (x_arr[1] > x_arr[0]) {
                        return x_arr[1] - x_arr[0];
                    } else {
                        return x_arr[0] - x_arr[1];
                    }
                }
            },
            line_captions: {
                // get captions straight from the attribute
                get: function () {
                    return this.getAttribute('line_captions').split(',');
                },

                // set the caption attribute
                set: function (newValue) {
                    this.setAttribute('line_captions', newValue.join(','));
                }
            },
            line_colors: {
                // get captions straight from the attribute
                get: function () {
                    return this.getAttribute('line_colors').split(',');
                },

                // set the caption attribute
                set: function (newValue) {
                    this.setAttribute('line_colors', newValue.join(','));
                }
            },
            extent_y: {
                // get extent straight from the attribute
                get: function () {
                    if (this.hasAttribute('extent_y')) {
                        var y_arr = this.getAttribute('extent_y').split(',');

                        var y_i = 0;
                        var y_len = y_arr.length;

                        while (y_i < y_len) {
                            y_arr[y_i] = parseInt(y_arr[y_i], 10);
                            y_i += 1;
                        }

                        return y_arr;

                    } else {
                        //get the minimum and maximum all the lines and set the minimum and maximum of the chart
                        var extent_i = 0;
                        var extent_len = this.arr_d3_data.length;
                        var curExtent = [];
                        var oldExtent;
                        var extent = [];

                        while (extent_i < extent_len) {
                            curExtent = d3.extent(this.arr_d3_data[extent_i], function (d) {
                                return parseInt(d.y, 10);
                            });

                            // if (this.getAttribute('chart_caption') === 'Connections') console.log('this.arr_d3_data[extent_i]:', this.arr_d3_data[extent_i]);

                            // if (this.getAttribute('chart_caption') === 'Connections') console.log('partial:', curExtent);

                            if (oldExtent) {
                                extent[0] = Math.min(curExtent[0], oldExtent[0], curExtent[1], oldExtent[1], 0);
                                extent[1] = Math.max(curExtent[0], oldExtent[0], curExtent[1], oldExtent[1], 0);
                            } else {
                                extent[0] = Math.min(curExtent[0], curExtent[1], 0);
                                extent[1] = Math.max(curExtent[0], curExtent[1], 0);
                            }

                            oldExtent = extent;

                            extent_i += 1;
                        }

                        if (this.hasAttribute('extent_y_reverse')) {
                            var temp = extent[0];
                            extent[0] = extent[1];
                            extent[1] = temp;
                        }
                        // if (this.getAttribute('chart_caption') === 'Connections') console.log('full:', extent);

                        return extent;
                    }
                },

                // set the extent attribute
                set: function (newValue) {
                    this.setAttribute('extent_y', newValue);
                }
            },
            extent_y_diff: {
                // get value from the other accessor
                get: function () {
                    var y_arr = this.extent_y;
                    if (y_arr[1] > y_arr[0]) {
                        return y_arr[1] - y_arr[0];
                    } else {
                        return y_arr[0] - y_arr[1];
                    }
                }
            },
        },
        methods: {

        }
    });
});

window.addEventListener('load', function () {
	document.getElementById('set_all_intervals').addEventListener('keyup', function () {
		var newValue = this.value;
		var arrElement = document.getElementsByClassName('refresh_interval');
		for (var i = 0, len = arrElement.length; i < len; i += 1) {
			arrElement[i].value = newValue;
			GS.triggerEvent(arrElement[i], 'change');
		}
	});
});
        </script>
    </head>
    <body>
        <gs-page>
            <gs-header>
                <center><h3>Activity Statistics</h3></center>
            </gs-header>
            <gs-body padded>
				<label for="discard">Discard data older than (seconds):</label>
				<gs-text id="discard" value="3600"></gs-text>
				<br />
				<br />
				<label for="set_all_intervals">Set all intervals:</label>
				<gs-text id="set_all_intervals" value="1"></gs-text>
				<br />
				<br />

                <gs-grid widths="1,1">
                    <gs-block>
                        <gs-chart-line chart_caption="Connections (point in time)" type="live" line_captions="Total,Active,Idle" line_colors="#6CC644,#1776B6,#D8241F" raw="SELECT current_time::time, count(*) AS total_connections, sum(CASE WHEN state = 'active' THEN 1 ELSE 0 END) AS active_connections, sum(CASE WHEN state = 'active' THEN 0 ELSE 1 END) AS inactive_connections FROM pg_catalog.pg_stat_activity" style="height: 400px;"></gs-chart-line>
						<br />
						<br />
                    </gs-block>
                    <gs-block>
                        <gs-chart-line chart_caption="Transactions (per X seconds)" type="live" append line_captions="Total,Commits,Rollback" line_colors="#6CC644,#1776B6,#D8241F" raw="SELECT current_time::time, sum(xact_commit) + sum(xact_rollback) AS total_transactions, sum(xact_commit) AS commit_transactions, sum(xact_rollback) AS rollback_transactions FROM pg_catalog.pg_stat_database" style="height: 400px;"></gs-chart-line>
						<br />
						<br />
                    </gs-block>
                </gs-grid>
                <gs-grid widths="1,1,1">
                    <gs-block>
                        <gs-chart-line chart_caption="Tuples (per X seconds)" type="live" append line_captions="Inserted,Updated,Deleted" line_colors="#6CC644,#1776B6,#D8241F" raw="SELECT current_time::time, sum(tup_inserted) AS total_inserted, sum(tup_updated) AS total_updated, sum(tup_deleted) AS total_deleted FROM pg_catalog.pg_stat_database" style="height: 400px;"></gs-chart-line>
                    </gs-block>
                    <gs-block>
                        <gs-chart-line chart_caption="Tuples (per X seconds)" type="live" append line_captions="Fetched,Returned" line_colors="#6CC644,#1776B6,#D8241F" raw="SELECT current_time::time, sum(tup_fetched) AS total_fetched, sum(tup_returned) AS total_returned FROM pg_catalog.pg_stat_database" style="height: 400px;"></gs-chart-line>
                    </gs-block>
                    <gs-block>
                        <gs-chart-line chart_caption="Block IO (per X seconds)" type="live" append line_captions="Read,Hit" line_colors="#6CC644,#1776B6,#D8241F" raw="SELECT current_time::time, sum(blks_read) AS total_read, sum(blks_hit) AS total_hit FROM pg_catalog.pg_stat_database" style="height: 400px;"></gs-chart-line>
                    </gs-block>
                </gs-grid>
            </gs-body>
        </gs-page>
        
        <script src="../js/settings.js" type="text/javascript"></script>
        <style id="customCss"></style>
    </body>
</html>
